#charset "us-ascii"

#include <tads.h>
#include <adv3.h>
#include <en_us.h>

#include <httpsrv.h>
#include <httpreq.h>

/*
 * This small demo was used to unearth a very mysterious HTTPServer bug.
 * The server would only answer requests a limited number of items.
 * Building the affordances list--especially when including TIActions that
 * take an indirect object--would shorten this time.
 *
 * Much thanks to TADS author Mike Roberts for helping with this one.
 * Turns out that--just as the TADS System Manual section on HTTPServer says--
 * you need to keep a reference to the HTTPServer to prevent it from getting
 * garbage collected.
 */

versionInfo: GameID
    IFID = 'badb2644-938f-43d2-9f04-ee63689e300e'
    name = 'Bug Isolation'
    byline = 'by Zach Tomaszewski'
    version = '1'
    desc = 'Put a brief "blurb" about your game here.'
    htmlDesc = 'Put a brief "blurb" about your game here.'
;

// --PART 1--
// Define a few silly world objects.

the_void : OutdoorRoom 'The Void'
    "Two parallel horizontal surfaces--one above you and one below you--stretch
    off in all directions."
;

+ Decoration 'x' 'X'
    "Apparently, it is marking a spot, perhaps even THE spot."
    specialDesc = "There is an X on the surface beneath your feet."
;

+ Thing 'hammer' 'hammer'
    "It is a tool for pounding things.  It is not a shovel."

    iobjFor(AttackWith) {
        verify() { logical;}
    }
;

+ Actor 'furball/hairball' 'hairball'
    "It is a fist-sized oblong of dense hair.
     You can feel it emanating a sense of smugness from up to 2 feet away.
     Up close, it smells a little like stale nachos and scorn."

    dobjFor(AttackWith) {
        action() {
            "The hairball is unfazed by your feeble attempts to harm it.";
        }
    }
;

me: Actor
    /* the initial location is the entryway */
    location = the_void
    spin = 0
;


// --PART 2--
// I want to get any input requests from a web-based user input system
// without too much interference with normal TADS operations.  So I'm
// hooking in at the point where TADS normally gets input strings.
//
// In the full system, I'd call another function from here that loops,
// processing resource web requests until it recieves a user command.
// It will then return this user command to readMainCommand to process.
//
// Right now, I'm skipping all of that actual processing except for the
// getNetEvent() call.
//
replace readMainCommand(which)
{
    // execute any pre-command-prompt daemons
    eventManager.executePrompt();  //as per original readMainCommand

    if (me.spin > 0) {
        getNetEvent(500); //timeout in ms  //COMMENT this out and all works fine
        me.spin--;
        return 'WAIT';  //could be any net-provided user command
    }else {
        me.spin = 3; //let local user have a turn for testing purposes and then reset
        //ORIGINAL:
        return inputManager.getInputLine(true, {: gLibMessages.mainCommandPrompt(which)});
    }
}


gameMain: GameMainDef
    /* the initial player character is 'me' */
    initialPlayerChar = me
    server = nil

    verbNames = [
        //no-obj
        ExitsAction -> ['Exits'],
        InventoryAction -> ['Inventory'],
        LookAction -> ['Look'],
        //one obj
        AttackAction -> ['Kill'],
        DropAction -> ['Drop'],
        ExamineAction -> ['Examine'],
        TakeAction -> ['Take'],
        //two objs
        AttackWithAction -> ['Break', 'with']   //TRY removing this action from list
    ]

    startServer() {
        //WAS: local server = new ...
        self.server = new HTTPServer(getLocalIP(), 49000);
        "HTTP Server listening on port <<server.getPortNum()>>\n";
    }

    showAffordances() {
        "You can currently do these things:\n";
        foreach (local verb in self.verbNames.keysToList()) {
            if (verb.ofKind(IAction)) {
                if (isAfforded(gPlayerChar, verb, nil, nil)) {
                    "<<self.verbNames[verb][1]>>\n";
                }
            }else if (verb.ofKind(TAction)) {
                local objs = gPlayerChar.scopeList();
                foreach (local dobj in objs) {
                    if (verb.ofKind(TIAction)) {
                        //also need iobj
                        foreach (local iobj in objs) {
                            if (isAfforded(gPlayerChar, verb, dobj, iobj)) {
                                "<<self.verbNames[verb][1]>> <<dobj.name>>
                                 <<self.verbNames[verb][2]>> <<iobj.name>>\n";
                            }
                        }
                    }else {  //simple TAction
                        if (isAfforded(gPlayerChar, verb, dobj, nil)) {
                            "<<self.verbNames[verb][1]>> <<dobj.name>>\n";
                        }
                    }
                }
            }
        }
    }

    isAfforded(actor, action, dobj, iobj) {

        action.actor_ = actor;
        if (action.ofKind(TAction)) {
            action.dobjCur_ = dobj;
        }
        if (action.ofKind(TIAction)) {
            action.iobjCur_ = iobj;
            action.tentativeDobj_ = [dobj];
            action.tentativeIobj_ = [iobj];
        }

        gAction = action;
        gActor = actor;

        local results = action.verifyAction();

        if (!results) {
            return 1; //no objections to the command
        }

        local mostLimiting = results.getEffectiveResult();
        local rank = mostLimiting.resultRank;
        if (mostLimiting.ofKind(InaccessibleVerifyResult) ||
            mostLimiting.ofKind(IllogicalVerifyResult)) {
            return 0;  //includes IllogicalSelf too
        }else if (mostLimiting.ofKind(LogicalVerifyResult)) {
            if (rank <= 0) rank = 1;
            return rank;   //+: allowed to degree of logical rank
        }else {
            //only partially afforded
            if (rank >= 0) rank = (rank * -1) - 1;
            return rank;   //-: weak affordance
        }
    }

    showIntro()
    {
        startServer();
        "Welcome to the Astral Plane!\b";
//        for (local i = 0; i < 50; i++) {
//            "<<i>>\n";
//            getNetEvent(500);    //runs fine!
//        }
    }

    showGoodbye()
    {
        "<.p>Farewell, Astral Voyager!\b";
    }
;

DefineIAction(Affordances)
    execAction() {
        gameMain.showAffordances();
    }
;
VerbRule(Affordances)
     'affordances' : AffordancesAction
;